package uk.co.irisys;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;

/**
 *
 * @author darren.steadman
 */
public class StandardPacket {

    public static final short ESC = 0xAA;
    public static final short SOF = 0xBB;
    public static final short EOF = 0xCC;
    
    private ByteArrayInputStream m_PacketInputStream;
    private ByteArrayOutputStream m_PacketOutputStream;
    private DataInputStream m_PacketReader;
    private DataOutputStream m_PacketWriter;
    //private CRC8 crc = new CRC8();

    public static StandardPacket MakeObject(){
        return new StandardPacket();
    }

    //---------------------------------------------
    public StandardPacket() {
        m_PacketOutputStream = new ByteArrayOutputStream();
        m_PacketInputStream = new ByteArrayInputStream(m_PacketOutputStream.toByteArray());
        
        m_PacketReader = new DataInputStream(m_PacketInputStream);
        m_PacketWriter = new DataOutputStream(m_PacketOutputStream);
    }

    /**
     *
     */
    private void WriteComplete(){
        m_PacketInputStream = new ByteArrayInputStream(m_PacketOutputStream.toByteArray());
       
        m_PacketReader = new DataInputStream(m_PacketInputStream); 
    }

    //---------------------------------------------
    public void WriteRawByte(short b) throws IOException{
        m_PacketWriter.writeByte(b);
        WriteComplete();
    }

    //-----------------------------------------------
    public void WriteByte(short b) throws IOException{
        WriteRawByte(b);
    }

    //----------------------------------------------
    public void WriteShort(int s) throws IOException{
        short byte1 = (short)(s & 0xFF);
        short byte2 = (short)((s >> 8) & 0xFF);
                
        WriteByte(byte1);
        WriteByte(byte2);
    }

    //------------------------------------------------
    public void WriteLong(long l) throws IOException{
        short byte1 = (short)(l & 0xFF);
        short byte2 = (short)((l >> 8) & 0xFF);
        short byte3 = (short)((l >> 16) & 0xFF);
        short byte4 = (short)((l >> 24) & 0xFF);

        WriteByte(byte1);
        WriteByte(byte2);
        WriteByte(byte3);
        WriteByte(byte4);
    }

    //-------------------------------------------------
    public int ReadByte() throws IOException{
        return m_PacketReader.readUnsignedByte();
    }

    //-------------------------------------------------
    public short ReadShort() throws IOException{
        int byte1 = ReadByte();
        int byte2 = ReadByte();
        
        short output = 0;
        output |= (short)byte1;
        output |= (short)byte2 << 8;

        return output;
    }

    //-------------------------------------------------
    public long ReadLong() throws IOException{
        int byte1 = ReadByte();
        int byte2 = ReadByte();
        int byte3 = ReadByte();
        int byte4 = ReadByte();
        
        long output = 0;
        output |= (long)byte1;
        output |= (long)byte2 << 8;
        output |= (long)byte3 << 16;
        output |= (long)byte4 << 24;

        return output;
    }

    //--------------------------------------------------
    public float ReadSingleFloat() throws IOException{
        int byte1 = ReadByte();
        int byte2 = ReadByte();
        int byte3 = ReadByte();
        int byte4 = ReadByte();

        long output = 0;
        output |= (long)byte1;
        output |= (long)byte2 << 8;
        output |= (long)byte3 << 16;
        output |= (long)byte4 << 24;

        return output;
    }

    //--------------------------------------------------
    public void SetPacket(int[] packetData) throws IOException {
        ResetPacket();

        for(int i = 0; i < packetData.length; i++){
            WriteByte((short)packetData[i]);
        }
    }

    //----------------------------------------------------
    public void StuffEscapeBytes() throws IOException {
        short[] unescaped_data = new short[m_PacketOutputStream.size()];

        for(int i = 0; i < m_PacketOutputStream.size(); i++){
            unescaped_data[i] = (short)m_PacketReader.readUnsignedByte();
        }

        m_PacketOutputStream.reset();
        WriteByte(unescaped_data[0]);

        for (int i = 1; i < unescaped_data.length; i++)
        {
            short s = unescaped_data[i];

            WriteByte(s);

            if (ESC == s)
            {
                WriteByte(s);
            }
        }
    }

    //---------------------------------------------------
    public byte[] GetPacket() {
        return m_PacketOutputStream.toByteArray();
    }

    //--------------------------------------------------
    public long GetPacketSize() {
        return m_PacketOutputStream.size(); // -7 ?
    }

    //------------------------------------------------
    public long GetCurrentReadPosition() {
        return (m_PacketOutputStream.size() - m_PacketInputStream.available());
    }

    //--------------------------------------------------

    //Go through the buffer first and extract the data section
    //and the crc section then check the crc
//    public boolean CheckCRC() {
//        byte[] packet = m_PacketOutputStream.toByteArray();
//        byte check = 0;
//        byte actualCrc = 0;
//        boolean passed = false;
//
//        for (int i = 0; i < packet.length - 1; i++) {
//            crc.PutByte(packet[i]);
//        }
//
//        check = packet[packet.length - 1];
//        actualCrc = crc.Done();
//
//        ResetReadPtr();
//
//        passed = (actualCrc == check);
//
//        if (!passed) {
//            //System.err.println("CRC fail");
//        }
//
//        return passed;
//    }

    //----------------------------------------------------
    public void ResetReadPtr() {
        try{
            m_PacketReader.reset();
            m_PacketInputStream.reset();
        }
        catch(IOException e){

        }
    }

    /// <summary>
    ///
    /// </summary>
    public void ResetPacket() {
        m_PacketOutputStream = new ByteArrayOutputStream();
        m_PacketInputStream = new ByteArrayInputStream(m_PacketOutputStream.toByteArray());

        m_PacketReader = new DataInputStream(m_PacketInputStream);
        m_PacketWriter = new DataOutputStream(m_PacketOutputStream);
    }

    //----------------------------------------------------

    //Make sure that we generate the crc on the correct section of code
    //Also when the crc is added to the buffer make sure that any addition 0xAA's
    //are added if needed
//    public void GenerateCRC() throws IOException{
//        byte[] packet = m_PacketOutputStream.toByteArray();
//        
//        for (int i = 2; i < packet.length; i++) {
//            crc.PutByte(packet[i]);
//        }
//
//        byte checksum = crc.Done();
//
//        WriteByte(checksum);        
//    }
}
